package classification;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import utilities.DCUtilities;

import model.Document;
import model.Neighbour;
import model.Term;

public class DocClassification {
	private ArrayList<Document> documents;
	private HashMap<String, Term> terms;
	
	public DocClassification(ArrayList<Document> docs, HashMap<String, Term> terms) {
		this.documents = docs;
		this.terms = terms;
	}
	
	public ArrayList<Document> getDocuments() {
		return documents;
	}
	public void setDocuments(ArrayList<Document> documents) {
		this.documents = documents;
	}
	public HashMap<String, Term> getTerms() {
		return terms;
	}
	public void setTerms(HashMap<String, Term> terms) {
		this.terms = terms;
	}
	
	public List<Neighbour> getNearesNeighbours(Document doc, int N) throws Exception {
		for (Document comparedDoc:documents){
			comparedDoc.setTempDistance(measureDistance(comparedDoc, doc));
		}
		Collections.sort(documents, new NeighbourComparator());
		ArrayList<Neighbour> neighbours = new ArrayList<Neighbour>();
		for (int i=0; i<N; i++){
			Document nDoc = documents.get(i);
			Neighbour neighbour = new Neighbour(nDoc, nDoc.getTempDistance());
			neighbours.add(neighbour);
		}
		return neighbours;
	}
	
	public double measureDistance(Document first, Document second) throws Exception {
		if(first == null || second == null)
			throw new Exception("Can not do evaluate the distance!");
		double distance = 0;
		HashMap<String, Double> tfidfsFirst = evalTFIDF(first);
		HashMap<String, Double> tfidfsSecond = evalTFIDF(second);
		
		distance = evalDistance(tfidfsFirst, tfidfsSecond);
		return distance;
	}
	
	public HashMap<String, Double> evalTFIDF(Document doc){
		return doc.getTfIdf(documents.size());
	}
	
	
	
	private double evalDistance(HashMap<String, Double> firstVector, HashMap<String, Double> secondVector){
		
		for (String key:firstVector.keySet()){
			if (!secondVector.containsKey(key))
				secondVector.put(key, new Double(0.0));
		}
		for (String key:secondVector.keySet()){
			if (!firstVector.containsKey(key))
				firstVector.put(key, new Double(0.0));
		}
		double fs = 0.0;
		double ff = 0.0;
		double ss = 0.0;
		
		double distanceChange = 0.0;
		
		for (String key:firstVector.keySet()){
			
			double fv = firstVector.get(key);
			double sv = secondVector.get(key);
			
			double fsTemp = Math.abs(fv*sv);
			double ffTemp = fv*fv;
			double ssTemp = sv*sv;
			
			if (sv<0 && fsTemp != 0.0){
				if (fv<0){
					distanceChange += DCUtilities.TITLE_TITLE_DISTANCE_CHANGER;
				}else {
					distanceChange += DCUtilities.TITLE_BODY_DISTANCE_CHANGER;
				}
			}
			fs += fsTemp;
			ff += ffTemp;
			ss += ssTemp;
			
		}
		
		distanceChange = 1.0 - distanceChange;
		
		distanceChange = distanceChange<0.0 ? 0.0 : distanceChange;
		
		if (ff == 0.0 || ss == 0.0){
			return 1.0;
		}
		
		double distance = distanceChange*(1.0 - fs/(Math.sqrt(ff)*Math.sqrt(ss)));
//		if (distance < 0){
//			return 0.0;
//		}
		return distance;
		
	}

}
